﻿using BootyBay.StoreAppMgmtCore.Models;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Windows.Management.Deployment;
 

namespace BootyBay.StoreAppMgmtCore
{

    public class AppMgr
    {
        public event AppMgmtEventHandler OnNotificationOutput;

        Windows.Management.Deployment.PackageManager packageManager = new Windows.Management.Deployment.PackageManager();

        private void PrintMsg(string msg)
        {
            if (OnNotificationOutput != null)
                OnNotificationOutput(this, new AppMgmtEventArgs() { EventMessage = msg });
        }

        public bool InstallCert(string cerPath)
        {
            Uri cerPathUri = DownloadPackageToLocalTempFile(new Uri(cerPath));

            X509Certificate2 cert = new X509Certificate2(cerPathUri.AbsolutePath);
            return InstallCert(cert);
        }

        public bool InstallCert(X509Certificate2 cert)
        {
            PrintMsg("Installing Certificate: " + cert.FriendlyName);
            try
            {
                X509Store store = new X509Store(StoreName.TrustedPeople, StoreLocation.LocalMachine);
                store.Open(OpenFlags.ReadWrite);
                store.Add(cert);
                store.Close();
                PrintMsg("Certificate deployed.");
                return true;
            }
            catch (Exception ex)
            {
                PrintMsg("Certificate deployment failed: " + ex.Message);
                return false;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="inputPackageUri"></param>
        /// <returns>isSucceeded</returns>
        public bool AddPackage(string inputPackageUri)
        {
            //string inputPackageUri = args[0];
            bool isSucceeded = false;
            
            try
            {
                Uri packageUri = new Uri(inputPackageUri);

                packageUri = DownloadPackageToLocalTempFile(packageUri);

               

                Windows.Foundation.IAsyncOperationWithProgress<DeploymentResult, DeploymentProgress> deploymentOperation = packageManager.AddPackageAsync(packageUri, null, Windows.Management.Deployment.DeploymentOptions.None);

                PrintMsg(string.Format("Installing package {0}", inputPackageUri));

                isSucceeded = HandleDeploymentOperation(deploymentOperation);
               
            }
            catch (Exception ex)
            {
                PrintMsg(string.Format("Installing package failed, error message: {0}", ex.Message));
                PrintMsg(string.Format("Full Stacktrace: {0}", ex.ToString()));

                isSucceeded = false;
            }

            return isSucceeded;
        }



        public bool UpdatePackage(string inputPackageUri)
        {
            //string inputPackageUri = args[0];
            bool isSucceeded = false;

            try
            {
                Uri packageUri = new Uri(inputPackageUri);

                packageUri = DownloadPackageToLocalTempFile(packageUri);

           

                Windows.Foundation.IAsyncOperationWithProgress<DeploymentResult, DeploymentProgress> deploymentOperation = packageManager.UpdatePackageAsync(packageUri, null, Windows.Management.Deployment.DeploymentOptions.None);

                PrintMsg(string.Format("Updating package {0}", inputPackageUri));

                isSucceeded = HandleDeploymentOperation(deploymentOperation);

            }
            catch (Exception ex)
            {
                PrintMsg(string.Format("Updating package failed, error message: {0}", ex.Message));
                PrintMsg(string.Format("Full Stacktrace: {0}", ex.ToString()));

                isSucceeded = false;
            }

            return isSucceeded;
        }

        public bool RemovePackage(string packageFullName)
        {
            //string inputPackageUri = args[0];
            bool isSucceeded = false;
            try
            {            
                Windows.Foundation.IAsyncOperationWithProgress<DeploymentResult, DeploymentProgress> deploymentOperation = packageManager.RemovePackageAsync(packageFullName);

                PrintMsg(string.Format("Removing package {0}", packageFullName));

                isSucceeded = HandleDeploymentOperation(deploymentOperation);

            }
            catch (Exception ex)
            {
                PrintMsg(string.Format("Removing package failed, error message: {0}", ex.Message));
                PrintMsg(string.Format("Full Stacktrace: {0}", ex.ToString()));

                isSucceeded = false;
            }

            return isSucceeded;
        }

        public bool RemovePackage(string inputPackageName, string inputPublisherName)
        {
            var pkg = FindPackage(inputPackageName, inputPublisherName);
            if (pkg != null)
            {
                return RemovePackage(pkg.AppPackageFullName);
            }
            else
                return false;
        }
        

        public UserAppPackageDataModel FindPackage(string inputPackageName, string inputPublisherName)
        {

            try
            {
                var packages = (IEnumerable<Windows.ApplicationModel.Package>)packageManager.FindPackages(inputPackageName, inputPublisherName); 
                //var packages = packageManager.FindPackages().ToList(); ;

                //foreach(var pkg in packages)
                //{
                //    if (pkg.Id.Publisher.Contains("Renjie"))
                //    {
                //        var fpkg = packageManager.FindPackages(pkg.Id.Name, pkg.Id.Publisher).ToList();
                //        var path = pkg.InstalledLocation;
                //    }
                //}

                    //.Where(p => p.DisplayName.Contains("TopAppShowcaseDemo")).ToList();
                //corrected from packageManager.FindPackages(inputPackageName, inputPublisherName); which requires admin privilege 
                var packageToFind = packages.FirstOrDefault();
                //performance risk here if there is a list of pkg finding requests.
                //TODO: add cache in memory

              //  var packageToFind = packages.Where(p => p.Id.Name == inputPackageName && p.Id.Publisher == inputPublisherName).FirstOrDefault();

                 
         
                if (packageToFind == null)
                {
                    PrintMsg("No packages were found.");
                    return null;
                }
                else
                {
                    PrintMsg("Package was found.");

                    UserAppPackageDataModel dataModel = new UserAppPackageDataModel(packageToFind);

                    return dataModel;
                }

            }
            catch (UnauthorizedAccessException)
            {
                PrintMsg("packageManager.FindPackages() failed because access was denied. This program must be run from an elevated command prompt.");
 
            }
            catch (Exception ex)
            {
                PrintMsg(string.Format("packageManager.FindPackages() failed, error message: {0}", ex.Message));
                PrintMsg(string.Format("Full Stacktrace: {0}", ex.ToString()));

                
            }

            return null;
        }

        private bool HandleDeploymentOperation(Windows.Foundation.IAsyncOperationWithProgress<DeploymentResult, DeploymentProgress> deploymentOperation)
        {
            bool isSucceeded = false;

            ManualResetEvent opCompletedEvent = new ManualResetEvent(false); // this event will be signaled when the deployment operation has completed.

            deploymentOperation.Completed = (depProgress, status) => { opCompletedEvent.Set(); };

            PrintMsg("Waiting for the operation to complete...");

            opCompletedEvent.WaitOne();

            if (deploymentOperation.Status == Windows.Foundation.AsyncStatus.Error)
            {
                Windows.Management.Deployment.DeploymentResult deploymentResult = deploymentOperation.GetResults();
                PrintMsg(string.Format("Operation Error: {0}", deploymentOperation.ErrorCode));
                PrintMsg(string.Format("Detailed Error Text: {0}", deploymentResult.ErrorText)); //  0x800B0109
                isSucceeded = false;
            }
            else if (deploymentOperation.Status == Windows.Foundation.AsyncStatus.Canceled)
            {
                PrintMsg("Operation Canceled");
                isSucceeded = false;
            }
            else if (deploymentOperation.Status == Windows.Foundation.AsyncStatus.Completed)
            {
                PrintMsg("Operation succeeded!");
                isSucceeded = true;
            }
            else
            {
                isSucceeded = false;
                PrintMsg("Operation status unknown");
            }

            return isSucceeded; 
        }

        private Uri DownloadPackageToLocalTempFile(Uri packageUri)
        {
            if (!packageUri.IsFile)
            {
                //download to local
                WebClient downloadWebClient = new WebClient();

                string tempFilePath = Path.GetTempFileName();

                PrintMsg("Downloading File...");
                downloadWebClient.DownloadFile(packageUri, tempFilePath);
                PrintMsg("File downloaded to " + tempFilePath);

                packageUri = new Uri(tempFilePath);

            }
            return packageUri;
        }

        public string EnableLoopbackExemptForApp(string pkgFamilyName)
        {
            //CheckNetIsolation.exe LoopbackExempt -a -n=4eee5a62-26ee-4c1f-ab12-64ef8a765cfa_j05ea6na1385w


            var proc = new Process();
            proc.StartInfo.FileName = "CheckNetIsolation.exe";
            proc.StartInfo.Arguments = "LoopbackExempt -a -n=" + pkgFamilyName;
            proc.Start();
            proc.WaitForExit();
            var exitCode = proc.ExitCode;
            proc.Close();
            return exitCode.ToString();
        }


    }
}
